﻿using System;
using System.Collections.Generic;
using System.Linq;
using Chatter.Actions;
using Fleck;
using Newtonsoft.Json;
using System.Web.Security;
using System.Web;
using System.Configuration;

namespace Chatter
{
    public class Server
    {
        private static List<User> _users;
        private static List<IWebSocketConnection> _sockets;

        public static void Main()
        {
            FleckLog.Level = LogLevel.Info;
            _users = new List<User>();
            _sockets = new List<IWebSocketConnection>();

            var server = new WebSocketServer( ConfigurationManager.AppSettings["endpoint"].ToString() );

            server.Start( socket =>
            {
                socket.OnOpen = () => ConnectionOpen( socket );
                socket.OnClose = () => ConnectionClose( socket );
                socket.OnMessage = message => MessageReceive( socket, message );
            } );

            var serverMessage = "";
            while ( serverMessage != "exit" )
            {
                BlastFromServer( serverMessage );
                serverMessage = Console.ReadLine();
            }

        }

        private static void BlastFromServer( string message )
        {
            Blast( new Message
                {
                    ActionId = ActionType.ChatMessage,
                    Content = message,
                    User = new User { ConnectionId = Guid.Empty, Name = "Server" }
                } );
        }

        private static void MessageReceive( IWebSocketConnection socket, string message )
        {
            var user = _users.Single( x => x.ConnectionId == socket.ConnectionInfo.Id );

            var request = JsonConvert.DeserializeObject<Message>( message );
            request.User = user;

            switch ( request.ActionId )
            {
                case ActionType.ChatMessage:
                    BlastFromUser( request, user );
                    break;
                case ActionType.UserList:
                    SendUserListToUser( socket );
                    break;
                default:
                    SendToUser( socket, "Yeah, I don't know what you are asking for..." );
                    break;
            }

            var msg = String.Format( request.Content ?? request.ActionId.ToString() );
            Console.WriteLine( msg );
        }

        private static void SendToUser( IWebSocketConnection socket, string message )
        {
            var data = JsonConvert.SerializeObject( new Message { ActionId = ActionType.ChatMessage, Content = message } );
            socket.Send( data );
        }

        private static void SendUserListToUser( IWebSocketConnection socket )
        {
            var userList = _users.Select( u => u.Name ).ToArray();
            var message = new Message
                {
                    ActionId = ActionType.UserList,
                    Content = JsonConvert.SerializeObject( userList )
                };

            string data = JsonConvert.SerializeObject( message );

            socket.Send( data );
        }

        private static void SendUserListToEveryone()
        {
            var userList = _users.ToArray();
            var message = new Message
            {
                ActionId = ActionType.UserList,
                Content = JsonConvert.SerializeObject( userList )
            };

            string data = JsonConvert.SerializeObject( message );
            foreach ( var socket in _sockets )
            {
                socket.Send( data );
            }
        }

        private static void BlastFromUser( Message message, User user )
        {
            Blast( message );
        }

        private static void Blast( Message message )
        {
            string data = JsonConvert.SerializeObject( message );
            foreach ( var socket in _sockets )
            {
                socket.Send( data );
            }
        }

        private static void Blast( string message )
        {
            var request = new Message { ActionId = ActionType.ChatMessage, Content = message };
            Blast( request );
        }


        private static void SendToUser( Message message, User user )
        {
            _sockets.Single( x => x.ConnectionInfo.Id == user.ConnectionId ).Send( JsonConvert.SerializeObject( message ) );
        }


        private static void ConnectionClose( IWebSocketConnection socket )
        {
            var user = _users.Single( x => x.ConnectionId == socket.ConnectionInfo.Id );
            Console.WriteLine( "{0} closed", user.Name ?? "unnamed user" );
            _sockets.Remove( socket );

            _users.Remove( user );

            SendUserListToEveryone();
            BlastFromServer( String.Format( "{0} has left the chat.", user.Name ) );
        }

        private static void ConnectionOpen( IWebSocketConnection socket )
        {
            Console.WriteLine( "Open from {0}", socket.ConnectionInfo.ClientIpAddress );
            _sockets.Add( socket );

            var ticket = FormsAuthentication.Decrypt( socket.ConnectionInfo.Cookies[".ASPXAUTH"] );
            var newUser = new User
                {
                    ConnectionId = socket.ConnectionInfo.Id,
                    Name = ticket != null ? ticket.Name : "unknown",
                };

            newUser.Name = newUser.Name.IndexOf( '@' ) > -1 ? newUser.Name.Substring( 0, newUser.Name.IndexOf( '@' ) ) : newUser.Name;
            _users.Add( newUser );

            var setupRequest = new Message { User = newUser, ActionId = ActionType.IdSet };
            SendToUser( setupRequest, newUser );

            Console.WriteLine( "{0} joined.", newUser.Name );
            SendUserListToEveryone();
            BlastFromServer( String.Format( "{0} has joined the chat!", newUser.Name ) );

        }
    }
}
